#ifndef __MQ_THREAD_POOL_HPP__
#define __MQ_THREAD_POOL_HPP__

#include <iostream>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <thread>
#include <functional>
#include <memory>
#include <atomic>
#include <future>
#include <utility>

namespace mq
{
    using func_t = std::function<void(void)>;

    class ThreadPool
    {
    public:
        using ptr=std::shared_ptr<ThreadPool>;
    private:
        void entry()
        {
            while (!_stop)
            {
                std::vector<func_t> tmp_tasks;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    _cond.wait(lock, [this]()
                               { return _stop || !_tasks.empty(); });
                    tmp_tasks.swap(_tasks); // 提高效率，一次唤醒，将任务全部获取，而不是一次唤醒就只获取一个任务
                }

                // 执行任务
                for (auto &task : tmp_tasks)
                {
                    task();
                }
            }
        }

    public:
        ThreadPool(int thread_count = 1)
            : _stop(false)
        {
            for (int i = 0; i < thread_count; i++)
            {
                _threads.emplace_back(&ThreadPool::entry, this);
            }
        }

        /*
            1.有了让压入线程池的任务类型不固定，这里采用模板;函数执行的参数也由外面根据函数类型自行传入,同时也采用可变参数列表
            2.push是将packaged_task形成的shared_ptr对象压入任务池执行func函数,需要返回future类型,所以push返回值需要推导
        */
        template <class F, class... Args>
        auto push(F &&func, Args &&...args) -> std::future<decltype(func(args...))>
        {
            // 1.推导形成构建packaged_task的返回值类型
            using res_type = decltype(func(args...));

            // 2.通过bind重新生成一个无参的函数类型
            auto nfunc = std::bind(std::forward<F>(func), std::forward<Args>(args)...);

            // 3.构建shared_ptr管理的packaged_task对象
            auto task = std::make_shared<std::packaged_task<res_type()>>(nfunc);

            // 4.返回用于同步关联packaged_task函数返回值的future对象
            std::future<res_type> ret = task->get_future();

            // 5.压入任务池
            {
                std::unique_lock<std::mutex> lock(_mutex);
                if (_stop == false)
                    _tasks.push_back([task]()
                                     { (*task)(); });
            }

            // 6.唤醒阻塞线程
            _cond.notify_all();
            return ret;
        }

        void stop()
        {
            if (_stop == true)
                return;
            _stop = true;
            _cond.notify_all();
            for (auto &iter : _threads)
                iter.join();
        }

        ~ThreadPool()
        {
            stop();
        }

    private:
        std::atomic<bool> _stop; // 原子性控制优雅退出的标志
        std::vector<func_t> _tasks;
        std::condition_variable _cond;     // 条件变量
        std::mutex _mutex;                 // 互斥锁
        std::vector<std::thread> _threads; // 线程池
    };
}

#endif